package com.yupi.yuapigateway;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yupi.yuapiclientsdk.utils.SignUtils;
import com.yupi.yuapicommon.model.entity.InterfaceInfo;
import com.yupi.yuapicommon.model.entity.User;
import com.yupi.yuapicommon.service.InnerInterfaceInfoService;
import com.yupi.yuapicommon.service.InnerUserInterfaceInfoService;
import com.yupi.yuapicommon.service.InnerUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 全局过滤
 *
 * @author <a href="https://github.com/liyupi">程序员鱼皮</a>
 * @from <a href="https://yupi.icu">编程导航知识星球</a>
 */
@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
    @DubboReference
    private InnerUserService innerUserService;
    @DubboReference
    private InnerInterfaceInfoService innerInterfaceInfoService;

    @DubboReference
    private InnerUserInterfaceInfoService innerUserInterfaceInfoService;
    private static final List<String> IP_WHITE_LIST = Arrays.asList("127.0.0.1","0:0:0:0:0:0:0:1");

    private static final String INTERFACE_HOST = "http://localhost:8123";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //TODO 1.用户请求到API网关
        //TODO 2.用户请求日志
        ServerHttpRequest request = exchange.getRequest();
        String path = INTERFACE_HOST + request.getPath().value();
        String method = request.getMethod().toString();


        log.info("请求唯一标识：" + request.getId());
        log.info("请求路径：" + path);
        log.info("请求方法：" + method);
        log.info("请求参数：" + request.getQueryParams());
        String sourceAddress = request.getLocalAddress().getHostString();
        log.info("请求来源地址：" + sourceAddress);
        log.info("请求来源地址：" + request.getRemoteAddress());
        ServerHttpResponse response = exchange.getResponse();
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        String clientIp = remoteAddress.getAddress().getHostAddress();
        log.info("请求IP："+clientIp);
        //TODO 3. 黑白名单
        if(!IP_WHITE_LIST.contains(clientIp)){
            log.info("请求来源地址：" + clientIp + " 不在白名单中，拒绝访问");
            response.setStatusCode(HttpStatus.FORBIDDEN); // 设置响应状态码为 403 Forbidden
            return response.setComplete(); // 结束请求
        }
        //TODO 4.用户鉴权 （判断ak、sk是否合法）
        HttpHeaders headers = request.getHeaders();
        String accessKey = headers.getFirst("accessKey");
        String nonce = headers.getFirst("nonce");
        String timestamp = headers.getFirst("timestamp");
        String sign = headers.getFirst("sign");
        String body = headers.getFirst("body");
        User invokeUser = null;
        try{
            invokeUser = innerUserService.getInvokeUser(accessKey);

        }catch (Exception e){
            log.error("获取用户信息失败", e);

            return handleError(response, HttpStatus.INTERNAL_SERVER_ERROR, "系统错误");

        }
        if (invokeUser == null) {
            return handleError(response, HttpStatus.FORBIDDEN, "无效的accessKey");
        }


        // 3.4 请求时间戳校验，时间和当前时间不能超过 5 分钟
        long currentTime = System.currentTimeMillis() / 1000;
        final long FIVE_MINUTES = 60 * 5L;
        if (Math.abs(currentTime - Long.parseLong(timestamp)) >= FIVE_MINUTES) {
            return handleError(response, HttpStatus.FORBIDDEN, "请求已过期");
        }

        if (Long.parseLong(nonce) > 10000L) {
            return handleError(response, HttpStatus.FORBIDDEN, "重复的请求");
        }

        // 3.6 签名验证
        String secretKey = invokeUser.getSecretKey();
        String serverSign = SignUtils.genSign(body, secretKey);
        if (!sign.equals(serverSign)) {
            return handleError(response, HttpStatus.FORBIDDEN, "签名验证失败");
        }
//        Mono<Void> filter = chain.filter(exchange);
        // 4. 请求的模拟接口是否存在，以及请求方法是否匹配
        InterfaceInfo interfaceInfo = null;
        try {
            interfaceInfo = innerInterfaceInfoService.getInterfaceInfo(path, method);
        } catch (Exception e) {
            log.error("getInterfaceInfo error", e);
        }
        if (interfaceInfo == null) {
            return handleError(response, HttpStatus.FORBIDDEN, "接口不存在");
        }

//        return handleResponse(exchange,chain, interfaceInfo.getId(), invokeUser.getId());
        ServerWebExchange modifiedExchange = exchange.mutate()
                .response(decorateResponse(exchange.getResponse(), interfaceInfo.getId(), invokeUser.getId()))
                .build();

        // 使用修改后的exchange继续过滤器链
        return chain.filter(modifiedExchange);


//        return  filter;



    }
    private ServerHttpResponse decorateResponse(ServerHttpResponse originalResponse, long interfaceInfoId, long userId) {
        return new ServerHttpResponseDecorator(originalResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<DataBuffer> fluxBody = (Flux<DataBuffer>) body;

                    return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
                        // 合并所有DataBuffer
                        DataBufferFactory dataBufferFactory = originalResponse.bufferFactory();
                        DataBuffer joinedBuffer = dataBufferFactory.join(dataBuffers);
                        byte[] content = new byte[joinedBuffer.readableByteCount()];
                        joinedBuffer.read(content);

                        // 释放资源
                        DataBufferUtils.release(joinedBuffer);
                        dataBuffers.forEach(DataBufferUtils::release);

                        // 处理响应数据
                        try {
                            String responseData = new String(content, StandardCharsets.UTF_8);
                            log.info("Response body: {}", responseData);

                            // 只有在响应成功时才增加调用次数
                            if (getStatusCode() == HttpStatus.OK) {
                                try {
                                    innerUserInterfaceInfoService.invokeCount(interfaceInfoId, userId);
                                    log.info("接口调用次数+1");
                                } catch (Exception e) {
                                    log.error("增加接口调用次数失败", e);
                                }
                            }

                            // 返回原始响应内容
                            return dataBufferFactory.wrap(content);

                        } catch (Exception e) {
                            log.error("处理响应数据失败", e);
                            return dataBufferFactory.wrap(content);
                        }
                    }));
                }
                // 如果body不是Flux，直接返回
                return super.writeWith(body);
            }};}


    @Override
    public int getOrder() {
        return -2;
    }
    private Mono<Void> handleError(ServerHttpResponse response, HttpStatus status, String message)  {


        // 1. 设置响应状态码
        response.setStatusCode(status);
        // 2. 设置响应的内容类型为 JSON
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);

        // 3. 构建错误响应对象
        ObjectMapper objectMapper = new ObjectMapper();
        Map<String, Object> result = new HashMap<>();
        result.put("code", status.value());
        result.put("message", message);
        try {
            // 4. 将错误响应对象转换为字节数组
            byte[] bytes = objectMapper.writeValueAsBytes(result);
            // 5. 创建数据缓冲区
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            // 6. 将数据写入响应
            return response.writeWith(Mono.just(buffer));
        } catch (JsonProcessingException e) {
            log.error("Error writing response", e);
            return response.setComplete();
        }
    }

}


    /**
     * 处理响应
     *
     * @param exchange
     * @param chain
     * @return
     */

